package com.surfilter.msmp.common.util;

import com.surfilter.msmp.common.util.httpclient.HttpClient43Fetch;
import com.surfilter.msmp.common.util.httpclient.Proxy;
import org.apache.commons.io.FileUtils;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 描述：Http请求相关方法
 * 作者: JinHuaTao
 * 时间：2017/6/29 15:22
 */
public class HttpUtil {

    private static final Logger LOG = LoggerFactory.getLogger(HttpUtil.class);

    public static final String NOT_FOUND = "-";

    public static String getHtmlByDefault(String url){
        String html = "";
        try{
            HttpGet get = initGetRequest(url);
            CloseableHttpResponse httpResponse = HttpClient43Fetch.execute(get);
            html = getContent(url, httpResponse);
            httpResponse.close();
        }catch (SocketTimeoutException e){
            LOG.debug("Socket端口连接超时" );
        }catch (HttpHostConnectException e){
            LOG.debug("找不到主机" );
        } catch (Exception e){

        }
        return html;
    }

    /**
     * 获取HTML页面方法
     * */
    public static String getHtml(String url){
        String html = "";
        try{
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig = RequestConfig
                    .custom()
                    .setCircularRedirectsAllowed(false)
                    .build();
            get.setConfig(requestConfig);
            //连接超时、连接请求超时配置默认 8s
            CloseableHttpResponse httpResponse = HttpClient43Fetch.execute(get);
            html = getContent(url, httpResponse);
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
        return html;
    }

    /**
     * 校验网站有效性
     * */
    public static int isEffect(String url){
        int flag = 0;
        try{
            HttpGet get = initGetRequest(url);
            CloseableHttpResponse response = HttpClient43Fetch.execute(get);
            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                flag = 1;
            }
        }catch (SSLHandshakeException e){
            LOG.debug("请求异常" + e);
        }catch (SocketTimeoutException e){
            LOG.debug("Socket端口连接超时" );
        }catch (HttpHostConnectException e){
            LOG.debug("主机HTTP连接异常" );
        }catch (NoHttpResponseException e){
            LOG.debug("主机没有HTTP响应" );
        }catch(UnknownHostException e){
            LOG.debug("未知主机" );
        }catch (NoRouteToHostException e){
            LOG.debug("解析不到主机" );
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
        return flag;
    }

    public static int isEffect(String url,int timeout){
        int effect = 0;
        try {
            HttpGet httpGet = HttpUtil.initGetRequest(url);
            CloseableHttpResponse response = HttpClient43Fetch.execute(httpGet, timeout);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                effect = 1;
            }
        } catch (Exception ignored) {
        }
        return effect;
    }
    /**
     * 获取HTML页面方法
     * */
    public static Map<String, String> getHeader(String url, String... headers){
        if(headers == null || headers.length == 0){
            return null;
        }
        Map<String, String> headerMap = new HashMap<>(headers.length);
        try{
            HttpGet get = initGetRequest(url);
            CloseableHttpResponse response = HttpClient43Fetch.execute(get);
            for (int i = 0; i < headers.length; i++){
                String key = headers[i];
                String value = "";
                Header[] tmpHeaders = response.getHeaders(key);
                if(tmpHeaders.length > 0){
                    value = tmpHeaders[0].getValue();
                }
                headerMap.put(key, value);
            }
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
        return headerMap;
    }

    public static String getHtml(String url, String ip, int port){
        String html = "";
        try{
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig = RequestConfig.custom().setCircularRedirectsAllowed(false)
                    .build();
            get.setConfig(requestConfig);
            CloseableHttpResponse httpResponse = HttpClient43Fetch.execute(get, new Proxy(ip, port));
            html = getContent(url, httpResponse);
        }catch (SSLHandshakeException e){
            LOG.debug("请求异常" + e);
        }catch (SocketTimeoutException e){
            LOG.debug(url + ",服务器Socket端口连接超时" );
        }catch (HttpHostConnectException e){
            LOG.debug("主机HTTP连接异常" );
        }catch (NoHttpResponseException e){
            LOG.debug("主机没有HTTP响应" );
        }catch(UnknownHostException e){
            LOG.debug("未知主机" );
        }catch (NoRouteToHostException e){
            LOG.debug("解析不到主机" );
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
        return html;
    }

    public static CloseableHttpResponse getHtmlResponse(String url, String ip, int port){
        try{
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig = RequestConfig.custom().setCircularRedirectsAllowed(false)
                    .build();
            get.setConfig(requestConfig);
            CloseableHttpResponse httpResponse = HttpClient43Fetch.execute(get, new Proxy(ip, port));
            return httpResponse;
        }catch (SSLHandshakeException e){
            LOG.debug("请求异常" + e);
        }catch (SocketTimeoutException e){
            LOG.debug(url + ",服务器Socket端口连接超时" );
        }catch (HttpHostConnectException e){
            LOG.debug("主机HTTP连接异常" );
        }catch (NoHttpResponseException e){
            LOG.debug("主机没有HTTP响应" );
        }catch(UnknownHostException e){
            LOG.debug("未知主机" );
        }catch (NoRouteToHostException e){
            LOG.debug("解析不到主机" );
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
      return null;
    }

    /**
     * 获取HTML页面方法
     * */
    public static CloseableHttpResponse getHtmlResponse(String url){
        try{
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig = RequestConfig
                    .custom()
                    .setCircularRedirectsAllowed(false)
                    .build();
            get.setConfig(requestConfig);
            //连接超时、连接请求超时配置默认 8s
            CloseableHttpResponse httpResponse = HttpClient43Fetch.execute(get);
            return httpResponse;
        }catch (Exception e){
            LOG.debug(">>>>>>>>>>>>>>>>" + url + "访问异常：" + e);
        }
        return null;
    }

    public static String getHtmlBySysProxy(String url){
        String html;
        String osName = System.getProperty("os.name").toLowerCase();
        //线下本地环境使用代理
        if(osName.contains("windows")){
            html = getHtml(url, "127.0.0.1", 1080);
        }else {
            html = getHtml(url);
        }
        return html;
    }



    public static CloseableHttpResponse getResponseBySysProxy(String url){
        CloseableHttpResponse htmlResponse=null;
        String osName = System.getProperty("os.name").toLowerCase();
        //线下本地环境使用代理
        if(osName.contains("windows")){
           htmlResponse = getHtmlResponse(url, "127.0.0.1", 1080);
        }else {
            htmlResponse = getHtmlResponse(url);
        }
        return htmlResponse;
    }

    public static HttpGet initGetRequest(String url) throws URISyntaxException {
        String tmpUrl = url.trim();
        URI uri = new URI(tmpUrl);
        HttpGet get = new HttpGet(uri);
        get.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        get.setHeader("Accept-Encoding", "gzip, deflate, sdch");
        get.setHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4");
        get.setHeader("Cache-Control", "no-cache");
        get.setHeader("Connection", "keep-alive");
        get.setHeader("Pragma", "no-cache");
        get.setHeader("Upgrade-Insecure-Requests", "1");
        get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36");
        return get;
    }

    /**
     * 获取Http请求响应内容
     * */
    public static String getContent(String url, HttpResponse httpResponse) throws IOException {
        String html = "";
        StatusLine statusLine = httpResponse.getStatusLine();
        Integer status = statusLine.getStatusCode();
        if(status == HttpStatus.SC_OK){
            String fileContentType = httpResponse.getEntity().getContentType().getValue();
            if(fileContentType.contains("text/html")||fileContentType.contains("text/javascript")){
                InputStream is = httpResponse.getEntity().getContent();
                BufferedInputStream bis = new BufferedInputStream(is);
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                byte[] data = new byte[1024];
                int current = 0;
                int size = 0;
                while((current = bis.read(data,0,data.length)) != -1){
                    buffer.write(data,0,current);
                    size += current;
                    //网页内存最大为5M，超过限制则退出
                    if(size > 5242880){
                        LOG.warn("==========================Error URL:" + url);
                        return html;
                    }
                }
                byte htmlByte[] =  buffer.toByteArray();
                String contentEncoding = TextUtil.getPageEncoding(htmlByte);
                html = new String(htmlByte, contentEncoding);
            }
        }else if(status == HttpStatus.SC_NOT_FOUND){
            html = NOT_FOUND;
        }

        return html;
    }





    public static Document getDocByJsoup(String url){
        String html = HttpUtil.getHtml(url);
        Document doc = Jsoup.parse(html);
        return doc;
    }

    /**
     * 禁用HTTPS请求的TLS证书验证的HTTP请求
     * */
    public static Document getDocumentByJsoup(String url){
        String html = getHtmlBySysProxy(url);
        return Jsoup.parse(html);
    }

    /**
     *  如果url不含有http则给其拼接上http
     *	如果url不含有https则给其拼接上https
     *	如果url不含有www.则给其拼接上www.
     *	返回真实可访问的URL,否则返回空
     * */
    public static List<String> getUrls(String domain){
        List<String> urlList = new ArrayList<>();
        if(domain == null || domain.trim().equalsIgnoreCase("")){
            return urlList;
        }
        String url = domain.trim();
        if(url.startsWith(TextUtil.HTTP_START_HEAD) || url.startsWith(TextUtil.HTTPS_START_HEAD)){
            urlList.add(url);
        }else if(TextUtil.correctIP(domain)){
            urlList.add(TextUtil.HTTPS_START_HEAD + url);
            urlList.add(TextUtil.HTTP_START_HEAD + url);
        }else {
            if(url.startsWith(TextUtil.WWW_START_HEAD)){
                urlList.add(TextUtil.HTTPS_START_HEAD + url);
                urlList.add(TextUtil.HTTP_START_HEAD + url);
            }else {
                urlList.add(TextUtil.HTTPS_START_HEAD + url);
                urlList.add(TextUtil.HTTPS_START_HEAD + TextUtil.WWW_START_HEAD  + url);
                urlList.add(TextUtil.HTTP_START_HEAD + url);
                urlList.add(TextUtil.HTTP_START_HEAD + TextUtil.WWW_START_HEAD  + url);
            }
        }
        return urlList;
    }


    /**
     * 向指定 URL 发送POST方法的请求
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            LOG.error("发送 POST 请求出现异常"+e);
        }finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                LOG.error("IO异常"+e);
            }
        }
        return result;
    }

    private static void getFileStream(InputStream inputStream,File file,String filepath) throws IOException {
        boolean newFile = file.createNewFile();
        if(newFile){
            FileUtils.copyInputStreamToFile(inputStream, file);
        }
    }


    /**
     * 下载常见的小工具及图标
     *
     * @param url     下载地址 https://github.com/shadowsocks/shadowsocks-libev/releases/download/v3.2.0/shadowsocks-libev-3.2.0.tar.gz
     * @param saveDir 文件保存目录  /fileRoot/sampleDownloadPath/shadowsocks/
     */
    public static String getFile(String url, String saveDir) {
        String fileSavePath = "";
        try {
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig;
            String osName = System.getProperty("os.name").toLowerCase();
            if (osName.contains("windows")) {
                LOG.info("检测是Windows系统，使用本地代理下载。");
                HttpHost httpHost = new HttpHost("127.0.0.1", 1080);
                requestConfig = RequestConfig.custom()
                        .setSocketTimeout(100000)
                        .setConnectTimeout(100000)
                        .setConnectionRequestTimeout(100000).setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                        .setProxy(httpHost)
                        .build();
            } else {
                requestConfig = RequestConfig.custom()
                        .setSocketTimeout(100000)
                        .setConnectTimeout(100000)
                        .setConnectionRequestTimeout(100000).setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                        .build();
            }
            get.setConfig(requestConfig);
            CloseableHttpClient httpClient = HttpClients.createDefault();
            long stTime = System.currentTimeMillis();
            HttpResponse httpResponse = httpClient.execute(get);
            StatusLine statusLine = httpResponse.getStatusLine();
            if (statusLine.getStatusCode() == 200) {
                InputStream inputStream = httpResponse.getEntity().getContent();
                long fileSize = httpResponse.getEntity().getContentLength();
                String fileContentType = httpResponse.getEntity().getContentType().getValue();
                LOG.info("============" + fileContentType);
                String tmpLastSuffix = "";
                if (fileContentType.contains("image")) {
                    tmpLastSuffix = ".png";
                } else if (fileContentType.contains("android")) {
                    tmpLastSuffix = ".apk";
                }

                String fileName = url.substring(url.lastIndexOf(FileUtil.DIR_SEPARATOR) + 1);
                if (!fileName.contains(".")) {
                    fileName += tmpLastSuffix;
                }
                if (fileName.length() > 50 || fileName.contains("?") || fileName.contains("!") || fileName.contains("&")) {
                    String tmpFileName = fileName;
                    fileName = System.currentTimeMillis() + "";
                    if (tmpFileName.contains(".")) {
                        int index = tmpFileName.lastIndexOf(".");
                        tmpLastSuffix = tmpFileName.substring(index);
                    }
                    fileName += tmpLastSuffix;
                    LOG.warn(tmpFileName + "包含特殊字符或长度太长，采用系统自命名:" + fileName);
                }
                FileUtil.mkDirTwo(saveDir);
                fileSavePath = saveDir + fileName;
                File file = new File(fileSavePath);
                try {
                    if (file.exists()) {
                        long tmpSize = file.length();
                        if (fileSize != tmpSize) {
                            getFileStream(inputStream, file, fileSavePath);
                        }
                    } else {
                        getFileStream(inputStream, file, fileSavePath);
                    }
                } catch (Exception e) {
                    LOG.info("文件写入异常" + statusLine.getStatusCode());
                } finally {
                    inputStream.close();
                }
                long edTime = System.currentTimeMillis();
                LOG.info("下载耗时：" + (edTime - stTime) + "ms");
                if(fileContentType.contains("webp")){
                    LOG.info("检测到时webp格式文件，转换为png图片格式");
                }
            } else {
                LOG.info("下载失败！下载结果码:" + statusLine.getStatusCode());
            }
        } catch (Exception e) {
            LOG.error("下载失败," + e);
        }
        return fileSavePath;
    }

    /**
     * 根据url路径获取文件保存路径, 只获取文件，不下载文件
     * */
    public static String getFileSavePath(String url, String saveDir) {
        String fileSavePath = "";
        try {
            HttpHost httpHost = new HttpHost("127.0.0.1", 1080);
            HttpGet get = initGetRequest(url);
            RequestConfig requestConfig;
            requestConfig = RequestConfig.custom()
                    .setSocketTimeout(100000)
                    .setConnectTimeout(100000)
                    .setConnectionRequestTimeout(100000).setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                    .setProxy(httpHost)
                    .build();
            get.setConfig(requestConfig);
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpResponse httpResponse = httpClient.execute(get);
            StatusLine statusLine = httpResponse.getStatusLine();
            if (statusLine.getStatusCode() == 200) {
                String fileContentType = httpResponse.getEntity().getContentType().getValue();
                LOG.info("==========================" + fileContentType);
                String tmpLastSuffix = "";
                if(fileContentType.contains("image")){
                    tmpLastSuffix = ".png";
                    if(fileContentType.contains("/")){
                        tmpLastSuffix = "." + fileContentType.substring(fileContentType.indexOf("/") + 1);
                    }
                }else if(fileContentType.contains("android")){
                    tmpLastSuffix = ".apk";
                }

                String fileName = url.substring(url.lastIndexOf(FileUtil.DIR_SEPARATOR) + 1);
                if(!fileName.contains(".")){
                    fileName += tmpLastSuffix;
                }
                if(fileName.length() > 50 || fileName.contains("?") || fileName.contains("!") || fileName.contains("&")){
                    String tmpFileName = fileName;
                    fileName = System.currentTimeMillis() + "";
                    if(tmpFileName.contains(".")){
                        int index = tmpFileName.lastIndexOf(".");
                        tmpLastSuffix = tmpFileName.substring(index);
                    }
                    fileName += tmpLastSuffix;
                    LOG.warn(tmpFileName + "包含特殊字符或长度太长，采用系统自命名:" + fileName);
                }
                fileSavePath = saveDir + fileName;
            } else {
                LOG.info("下载失败！下载结果码:" + statusLine.getStatusCode());
            }
        } catch (Exception e) {
            LOG.error("下载失败," + e);
        }
        return fileSavePath;
    }

    private static String getImageType(InputStream inputStream) throws IOException {
        byte[] b = new byte[4];
        inputStream.read(b, 0, b.length);
        String type = toHexString(b).toUpperCase();
        if (type.contains("FFD8FF")) {
            return "jpg";
        } else if (type.contains("89504E47") || type.contains("PNG")) {
            return "png";
        } else if (type.contains("47494638")) {
            return "gif";
        } else if (type.contains("424D")) {
            return "bmp";
        } else if (type.contains("RIFF")) {
            return "webp";
        } else {
            return "unknown";
        }
    }

    public static String toHexString(byte[] bytes)
    {
        StringBuilder sb=new StringBuilder();
        sb.append(new String(bytes));
        return sb.toString();
    }

    /**
     * 获取跳转后的地址
     * 如http://t.cn/EPckIYo
     * */
    public static String getRedirectUrl(String url){
        String location = "";
        try{
            URL serverUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
            conn.setRequestMethod("GET");
            // 必须设置false，否则会自动redirect到Location的地址
            conn.setInstanceFollowRedirects(false);
            conn.addRequestProperty("Accept-Charset", "UTF-8;");
            conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");
            conn.connect();
            location = conn.getHeaderField("Location");
        }catch (Exception e){
            LOG.error("获取重定向地址失败," + e);
        }

        return location;
    }


    /**
     * @Author jiabo
     * @Description get方式请求url
     **/
    public static String requestByGet(String url){
        HttpClient client = HttpClients.createDefault();
        HttpGet get = new HttpGet(url);
        try {
            HttpResponse response = client.execute(get);
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "UTF-8");
            return result;
        } catch (IOException e) {
           LOG.error("requestByGet请求失败"+url);
            return null;
        }
    }

    /***
     * 对于域名，可检测该域名是否能解析到DNS上的IP，以及该IP能否访问
     * 对于IP,可检测该IP能够访问
     * */
    public static boolean isReachable(String domain, int timeout){
        boolean isReach = false;
        try {
            InetAddress inetAddress = InetAddress.getByName(domain);
            isReach = inetAddress.isReachable(timeout);
        } catch (IOException e) {
//            e.printStackTrace();
        }
        return isReach;
    }

    /**
     *  正常网络： 2s 超时足够
     *  线上网络： 设置4s
     * */
    public static boolean isReachable(String domain){
        int timeout = 4000;
        return isReachable(domain, timeout);
    }

    /**
     * 判断html网页中的title是否和oldTitle相同
     * */
    public static boolean titleEq(String oldTitle, String html){
        Document doc = Jsoup.parse(html);
        String title = doc.title();
        boolean noChange = false;
        if(oldTitle.equals(title)){
            noChange = true;
        }
        return noChange;
    }

    /**
     * 手动销毁http客户端
     * */
    public static void destoryClient(String domain){
        HttpClient43Fetch.destoryDomain(domain);
    }

    public static void main(String[] args) throws IOException {
        //https://195.160.231.158/  配置sslContext拿不到
        //https://yesssr.rocks/  配置sslContext可拿到
        String homeUrl = "https://github.com/asdfgt5/djy/blob/master/gb/n24hr.md?fldfcbzs#1";
        long st = System.currentTimeMillis();
        System.out.println(getHtml(homeUrl));
        long et = System.currentTimeMillis();
        System.out.println(">>>>>>" + (et - st) + "ms");

    }
}
